#include "group_module.h"

#include "burst_module.h"
#include "login_module.h"
#include "protocol_client.h"
#include "repo.h"
#include "session_module.h"
#include "weilapb.h"
#include "weilapb_defines.h"
#include "wlsdk_define.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
    wlsdk_message_callback callback;
} wl_group_global;

static wl_group_global s_group_global;

static void process_sync_group_version(void* data, wl_int32 datalen);
static void process_sync_group_info(void* data, wl_int32 datalen);
static wl_int32
    group_protocol_client_request_callback(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static wl_int32 group_protocol_client_listen_callback(PBWeilaMsg* message, void* context);
static void process_sync_group_version_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static void process_sync_group_infos_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context);
static wl_int32 group_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen);

static void group_sdk_message_callback(wl_uint32 msgid, wl_int32 resultcode, void* msgdata, wl_uint32 datalen) {
    if (s_group_global.callback) {
        s_group_global.callback(msgid, resultcode, msgdata, datalen);
    }
}

static wl_int32 group_del_groups(wl_uint64* groupIds, wl_uint32 count) {
    wl_uint32         index = 0;
    burst_sessioninfo sessioninfo;
    for (index = 0; index < count; index++) {
        sessioninfo.sessionid   = groupIds[index];
        sessioninfo.sessiontype = WLSDK_SESSION_TYPE_GROUP;
        wl_core_send_message(WL_MODULE_BURST, BURST_MSG_REMOVE_SESSION, &sessioninfo, sizeof(burst_sessioninfo));
    }
    return repo_del_groups(groupIds, count);
}

wl_int32 group_module_init(wlsdk_message_callback callback) {
    wl_int32  ret = 0;
    wl_module module;

    WLLOGI("group_module_init");
    memset(&module, 0, sizeof(wl_module));
    module.moduleid = WL_MODULE_GROUP;
    module.taskid   = WL_TASK_APP;
    module.callback = group_message_callback;
    ret             = wl_core_register_module(&module);
    if (0 != ret) {
        WLLOGW("wl_core_register_module login fail");
        return ret;
    }
    s_group_global.callback = callback;

    protocol_client_add_listen(
        WL_GROUP_NEW_MEMBER_NTF_MSG, WEILA_MSG_IND, group_protocol_client_listen_callback, wl_null);
    protocol_client_add_listen(WL_GROUP_DEL_GROUP_MSG, WEILA_MSG_IND, group_protocol_client_listen_callback, wl_null);
    protocol_client_add_listen(WL_GROUP_DEL_MEMBER_MSG, WEILA_MSG_IND, group_protocol_client_listen_callback, wl_null);
    protocol_client_add_listen(WL_GROUP_DEL_MEMBER_MSG, WEILA_MSG_IND, group_protocol_client_listen_callback, wl_null);
    protocol_client_add_listen(
        WL_GROUP_CHANGE_GROUP_ATTR_MSG, WEILA_MSG_IND, group_protocol_client_listen_callback, wl_null);
    protocol_client_add_listen(
        WL_GROUP_CHANGE_MEMBER_INFO_MSG, WEILA_MSG_IND, group_protocol_client_listen_callback, wl_null);
    return ret;
}

wl_int32 group_module_end(void) {
    WLLOGI("group_module_end");
    wl_core_unregister_module(WL_MODULE_GROUP);
    s_group_global.callback = wl_null;
    return 0;
}

static void sync_group(wl_uint64* groupids, wl_uint32 groupcount) {
    wl_uint32       memlen  = sizeof(group_sync_ids) + groupcount * sizeof(wl_uint64);
    group_sync_ids* syncids = (group_sync_ids*)wl_malloc(memlen);
    if (syncids) {
        syncids->count = groupcount;
        memcpy(syncids->ids, groupids, sizeof(wl_uint64) * groupcount);
        wl_core_send_message(WL_MODULE_GROUP, GROUP_MSG_SYNC_GROUP_INFO, syncids, memlen);
        wl_free(syncids);
    }
}

static void process_get_groups(void* data, wl_int32 datalen) {
    wl_list* group_list = repo_get_group_list();
    wl_int32 count      = wl_list_size(group_list);
    if (count > 0) {
        wl_uint32         groups_size = sizeof(wlsdk_group_list) + sizeof(wlsdk_group) * count;
        wlsdk_group_list* groups      = (wlsdk_group_list*)wl_malloc(groups_size);
        wl_uint32         pos         = 0;
        if (groups) {
            memset(groups, 0, sizeof(wlsdk_group_list));
            for (pos = 0; pos < count; pos++) {
                repo_group* group = (repo_group*)wl_list_get(group_list, pos);
                memcpy(&groups->groups[pos], &group->group, sizeof(wlsdk_group));
                groups->count++;
            }
            group_sdk_message_callback(WLSDK_MSG_GROUP_GET_GROUPS, WLSDK_RESULT_SUCCESS, groups, groups_size);
            wl_free(groups);
            return;
        }
    }
    group_sdk_message_callback(WLSDK_MSG_GROUP_GET_GROUPS, WLSDK_RESULT_SUCCESS, wl_null, 0);
}

static wl_int32 group_message_callback(wl_int32 msgid, void* data, wl_uint32 datalen) {
    WLLOGD("msgid %d", msgid);
    switch (msgid) {
        case GROUP_MSG_GET_GROUPS:
            process_get_groups(data, datalen);
            break;
        case GROUP_MSG_SYNC_GROUP_VERSION:
            process_sync_group_version(data, datalen);
            break;
        case GROUP_MSG_SYNC_GROUP_INFO:
            process_sync_group_info(data, datalen);
            break;
        default:
            WLLOGW("msgid %d not support", msgid);
            break;
    }
    return 0;
}

static wl_int32
    group_protocol_client_request_callback(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    WLLOGD("message %d resultCode %d", message->message, message->resultCode);
    switch (message->message) {
        case WL_GROUP_GET_EMBEDDED_GROUP_VER_MSG:
            process_sync_group_version_message(message, content, contentlen, context);
            break;
        case WL_GROUP_GET_EMBEDDED_INFOS:
            process_sync_group_infos_message(message, content, contentlen, context);
            break;
        default:
            break;
    }
    return 0;
}

static wl_int32 group_protocol_client_listen_callback(PBWeilaMsg* message, void* context) {
    WLLOGD("message %d", message->message);
    switch (message->message) {
        case WL_GROUP_NEW_MEMBER_NTF_MSG: {
            PBNewMemberNtf* notify = (PBNewMemberNtf*)message->pContent;
            repo_group      group;
            // 群不存在
            if (-1 == repo_get_group(notify->groupId, &group)) {
                sync_group(&notify->groupId, 1);
            }
            break;
        }
        case WL_GROUP_DEL_GROUP_MSG: {
            PB_UINT64* groupId = (PB_UINT64*)message->pContent;
            group_del_groups(groupId, 1);
            group_sdk_message_callback(WLSDK_MSG_GROUP_CHANGED, WLSDK_RESULT_SUCCESS, wl_null, 0);
            break;
        }
        case WL_GROUP_DEL_MEMBER_MSG: {
            PBDelMemberNtf* notify = (PBDelMemberNtf*)message->pContent;
            if (notify->userList.listCount) {
                if (PB_DATA_TYPE_ARRAY_INT32 == notify->userList.arrayType
                    || PB_DATA_TYPE_ARRAY_UINT32 == notify->userList.arrayType) {
                    wl_uint32* member_ids = (wl_uint32*)notify->userList.data;
                    wl_uint32  pos        = 0;
                    repo_user  userinfo;
                    repo_get_selfinfo(&userinfo);
                    for (pos = 0; pos < notify->userList.listCount; pos++) {
                        // 用户被删
                        if (member_ids[pos] == userinfo.user.userid) {
                            group_del_groups(&notify->groupId, 1);
                        }
                    }
                }
            }
            group_sdk_message_callback(WLSDK_MSG_GROUP_CHANGED, WLSDK_RESULT_SUCCESS, wl_null, 0);
            break;
        }
        case WL_GROUP_CHANGE_GROUP_ATTR_MSG: {
            repo_group   group;
            PBGroupAttr* groupinfo = (PBGroupAttr*)message->pContent;
            if (0 == repo_get_group(groupinfo->groupId, &group)) {
                repo_session sessioninfo;
                strncpy(group.group.group_name, groupinfo->name, sizeof(group.group.group_name));
                group.group.group_name[sizeof(group.group.group_name) - 1] = '\0';
                group.group.group_type                                     = groupinfo->groupType;
                group.group.ownerid                                        = groupinfo->ownerId;
                group.group.bursttype                                      = groupinfo->burstType;
                group.group.shutupstatus                                   = groupinfo->speechPermission;
                WLLOGW("group_name %s", group.group.group_name);
                repo_add_groups(&group, 1);
                group_sdk_message_callback(WLSDK_MSG_GROUP_CHANGED, WLSDK_RESULT_SUCCESS, wl_null, 0);
            } else {
                sync_group(&groupinfo->groupId, 1);
            }
            break;
        }
        case WL_GROUP_CHANGE_MEMBER_INFO_MSG: {
            PBMemberChangeNtf* notify = (PBMemberChangeNtf*)message->pContent;
            repo_user          userinfo;
            repo_get_selfinfo(&userinfo);
            // 用户群属性变更
            if (notify->memberInfo.userId == userinfo.user.userid) {
                repo_group         group;
                PBMemberChangeNtf* memberinfo = (PBMemberChangeNtf*)message->pContent;
                if (0 == repo_get_group(memberinfo->groupId, &group)) {
                    group.group.tts                  = memberinfo->memberInfo.tts;
                    group.group.locationshare        = memberinfo->memberInfo.sharedLocation;
                    group.group.shieldstatus         = memberinfo->memberInfo.recvStatus;
                    group.group.member_type          = memberinfo->memberInfo.memberType;
                    group.group.member_prority       = memberinfo->memberInfo.priority;
                    group.group.member_shutupstatus  = memberinfo->memberInfo.speechPermission;
                    group.group.member_shutuptimeout = memberinfo->memberInfo.silentDuration;
                    WLLOGW("tts %d share %d shield %d member type %d prority %d shutupstatus %d timeout %d",
                           memberinfo->memberInfo.tts,
                           memberinfo->memberInfo.sharedLocation,
                           memberinfo->memberInfo.recvStatus,
                           memberinfo->memberInfo.memberType,
                           memberinfo->memberInfo.priority,
                           memberinfo->memberInfo.speechPermission,
                           group.group.member_shutuptimeout);
                    repo_add_groups(&group, 1);
                    group_sdk_message_callback(WLSDK_MSG_GROUP_CHANGED, WLSDK_RESULT_SUCCESS, wl_null, 0);
                } else {
                    sync_group(&memberinfo->groupId, 1);
                }
            }
            break;
        }
        default:
            break;
    }
    return 0;
}

static void process_sync_group_version(void* data, wl_int32 datalen) {
    PBEncResult enc_result = weilapb_buildEmbeddedGetGroupVersionReq(REPO_GROUP_MAX);
    wl_int32    resultcode = protocol_client_send_request(
        &enc_result, wl_null, 0, PROTOCOL_CLIENT_REQUEST_TIMEOUT_60, group_protocol_client_request_callback, wl_null);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
        protocol_client_disconnect();
    }
}

static void process_sync_group_info(void* data, wl_int32 datalen) {
    group_sync_ids* syncids    = (group_sync_ids*)data;
    PBEncResult     enc_result = weilapb_buildEmbeddedGetGroupInfoReq(syncids->ids, syncids->count);
    wl_int32        resultcode = protocol_client_send_request(
        &enc_result, wl_null, 0, PROTOCOL_CLIENT_REQUEST_TIMEOUT_60, group_protocol_client_request_callback, wl_null);
    if (0 > resultcode) {
        WLLOGW("protocol_client_send_request error");
        protocol_client_disconnect();
    }
}

void process_sync_group_version_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    WLLOGI("resultCode %d", message->resultCode);
    if (WLSDK_RESULT_SUCCESS == message->resultCode) {
        PBGetGroupVersionRsp* response         = (PBGetGroupVersionRsp*)message->pContent;
        wl_list*              local_group_list = repo_get_group_list();
        wl_uint64*            add_group_ids    = (wl_uint64*)wl_malloc(sizeof(wl_uint64) * REPO_GROUP_MAX);
        wl_uint32             add_group_index  = 0;
        wl_uint64*            del_group_ids    = wl_null;
        wl_uint32             del_group_index  = 0;
        repo_group*           group_info       = wl_null;
        PBLinkItem*           item             = wl_null;
        wl_list_iterator      iterator;

        if (wl_null == add_group_ids) {
            WLLOGW("malloc add_group_ids fail");
            wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
            return;
        }
        del_group_ids = (wl_uint64*)wl_malloc(sizeof(wl_uint64) * REPO_GROUP_MAX);
        if (wl_null == del_group_ids) {
            WLLOGW("malloc del_group_ids fail");
            wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
            return;
        }
        // 遍历local group,获取已删除群Id
        group_info = (repo_group*)wl_list_get_first(local_group_list, &iterator);
        while (!wl_list_iterator_is_end(&iterator)) {
            wl_bool exist = wl_false;
            item          = response->pLinks;
            while (item) {
                PBGroupSimpleInfo* version_info = (PBGroupSimpleInfo*)item->data;
                // 本地群有效
                if (version_info->groupId == group_info->group.groupid) {
                    exist = wl_true;
                    WLLOGI("group %lld exist", version_info->groupId);
                    break;
                }
                item = item->pNext;
            }
            // 群删除
            if (!exist && del_group_index < REPO_GROUP_MAX - 1) {
                del_group_ids[del_group_index] = group_info->group.groupid;
                del_group_index++;
                WLLOGI("group %lld delete count %d", group_info->group.groupid, del_group_index);
            }
            group_info = (repo_group*)wl_list_get_next(&iterator);
        }
        // 删除群
        group_del_groups(del_group_ids, del_group_index);

        // 遍历服务器群,获取要更新群Id
        item = response->pLinks;
        while (item) {
            wl_bool            changed      = wl_true;
            PBGroupSimpleInfo* version_info = (PBGroupSimpleInfo*)item->data;
            group_info                      = (repo_group*)wl_list_get_first(local_group_list, &iterator);
            while (!wl_list_iterator_is_end(&iterator)) {
                // 群未变更
                if (version_info->groupId == group_info->group.groupid
                    && version_info->groupVersion == group_info->group.group_version
                    && version_info->memberVersion == group_info->group.member_version) {
                    changed = wl_false;
                    WLLOGI("version info groupinfo id %d version %d "
                           "memberversion %d",
                           group_info->group.groupid,
                           group_info->group.group_version,
                           group_info->group.member_version);
                    break;
                }
                group_info = (repo_group*)wl_list_get_next(&iterator);
            }
            // 群变更
            if (changed) {
                add_group_ids[add_group_index++] = version_info->groupId;
                WLLOGI("group %lld changed count %d all count %d",
                       version_info->groupId,
                       add_group_index,
                       response->listCount);
            }
            item = item->pNext;
        }
        // 同步群
        if (add_group_index) {
            sync_group(add_group_ids, add_group_index);
        } else {
            // 附着群组
            wl_core_send_message(WL_MODULE_SESSION, SESSION_MSG_ATTACH_SESSION, wl_null, 0);
            protocol_client_set_session_depend(PROTOCOL_CLIENT_SESSION_DEPEND_GROUP);
        }
        wl_free(add_group_ids);
        wl_free(del_group_ids);
    } else {
        wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
        WLLOGW("result %d", message->resultCode);
    }
}

void process_sync_group_infos_message(PBWeilaMsg* message, void* content, wl_uint32 contentlen, void* context) {
    WLLOGI("resultCode %d", message->resultCode);
    if (WLSDK_RESULT_SUCCESS == message->resultCode) {
        PBGetEmbeddedGroupInfoRsp* response = (PBGetEmbeddedGroupInfoRsp*)message->pContent;
        if (response->listCount) {
            wl_uint32 size = response->listCount > REPO_GROUP_MAX ? REPO_GROUP_MAX : response->listCount;
            if (size) {
                repo_group* groupinfos = (repo_group*)wl_malloc(sizeof(repo_group) * size);
                wl_uint32   index      = 0;
                PBLinkItem* item       = wl_null;
                if (wl_null == groupinfos) {
                    WLLOGW("malloc fail");
                    wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
                    return;
                }
                item = response->pLinks;
                while (item && index < REPO_GROUP_MAX) {
                    PBEmbeddedGroupInfo* groupinfo = (PBEmbeddedGroupInfo*)item->data;
                    WLLOGW("groupname %s", groupinfo->name);
                    // 固定群
                    // if (1 == groupinfo->type) {
                    groupinfos[index].group.groupid       = groupinfo->groupId;
                    groupinfos[index].group.group_version = groupinfo->version;
                    strncpy(groupinfos[index].group.group_name,
                            groupinfo->name,
                            sizeof(groupinfos[index].group.group_name));
                    groupinfos[index].group.group_name[sizeof(groupinfos[index].group.group_name) - 1] = '\0';
                    strncpy(groupinfos[index].group.group_number,
                            groupinfo->number,
                            sizeof(groupinfos[index].group.group_number));
                    groupinfos[index].group.group_number[sizeof(groupinfos[index].group.group_number) - 1] = '\0';
                    groupinfos[index].group.group_type           = groupinfo->type;
                    groupinfos[index].group.ownerid              = groupinfo->ownerId;
                    groupinfos[index].group.bursttype            = groupinfo->burstType;
                    groupinfos[index].group.shutupstatus         = groupinfo->speechPermission;
                    groupinfos[index].group.shieldstatus         = groupinfo->recvStatus;
                    groupinfos[index].group.tts                  = groupinfo->tts;
                    groupinfos[index].group.locationshare        = groupinfo->sharedLocation;
                    groupinfos[index].group.member_version       = groupinfo->memberVersion;
                    groupinfos[index].group.member_count         = groupinfo->memberCount;
                    groupinfos[index].group.member_type          = groupinfo->memberType;
                    groupinfos[index].group.member_prority       = groupinfo->priority;
                    groupinfos[index].group.member_shutupstatus  = groupinfo->selfSpeechPermission;
                    groupinfos[index].group.member_shutuptimeout = groupinfo->shutupTimeout;
                    index++;
                    // }
                    item = item->pNext;
                }
                repo_add_groups(groupinfos, index);
                wl_free(groupinfos);
            }
        }
        protocol_client_set_session_depend(PROTOCOL_CLIENT_SESSION_DEPEND_GROUP);
        group_sdk_message_callback(WLSDK_MSG_GROUP_CHANGED, WLSDK_RESULT_SUCCESS, wl_null, 0);
        // 附着群组
        wl_core_send_message(WL_MODULE_SESSION, SESSION_MSG_ATTACH_SESSION, wl_null, 0);
        wl_core_send_message(WL_MODULE_BURST, BURST_MSG_CHECK_SESSION, wl_null, 0);
        wl_core_send_message(WL_MODULE_SESSION, SESSION_MSG_GET_SESSIONS, wl_null, 0);
    } else {
        wl_core_send_message(WL_MODULE_LOGIN, LOGIN_MSG_CLOSE_PROTOCOL_CLIENT, wl_null, 0);
        WLLOGW("result %d", message->resultCode);
    }
}

#ifdef __cplusplus
}
#endif
